#include <iostream>
#include <fstream>
#include <sstream>
#include <cmath>

#include <auxiliares.h>
#include <calibracion.h>
#include <matriz.h>
#include <paths.h>

using namespace std;

/* IMPORTANTE:
 * Como suponemos que las i-esimas imagenes de cada tipo tienen el mismo vector de iluminacion, 
 * para obtenerlos procesamos las imagenes de la esfera y no las de la imagen a procesar */

/* Variables globales */

double radio_esfera = 0;
pair<int, int> centro_esfera(0, 0);

void obtenerCentroX(matriz<uint8_t> m)
{
	int cantDistintos = 0;
	int res = 0;
	bool termine_todo = false;
	bool encontre_dist_cero = false;

	for(int i = 0; (i < FILAS_IMG) && !termine_todo; i++)
	{
		for(int j = 0; (j < COLUMNAS_IMG) && !termine_todo; j++)
		{
			// busco el primer cero
			
			if(!encontre_dist_cero) 	//si todavia no entontre distinto de cero me fijo que hago.
			{
				if(m[i][j] != 0) 		// si es distinto de cero empiezo a obtener los puntos que me interesan
				{  
					encontre_dist_cero = true;
					cantDistintos++;
					res = j;
				}											
			}
			else 	// si ya encontre alguno distinto de cero me fijo si sigo leyendo distinto de ceros.
			{ 
				if((m[i][j]) != 0) cantDistintos++; 	// si es distinto incremento
				else termine_todo = true; 				// sino ya no me interesa seguir.
			}
		}
	}
	
	centro_esfera.first = res + (cantDistintos/2);
}

void obtenerCentroY(matriz<uint8_t> m)
{
	int cantDistintos = 0;
	int res = 0;
	bool termine_todo = false;
	bool encontre_dist_cero = false;

	for(int j=0; (j<COLUMNAS_IMG) && !termine_todo ; j++)
	{
		for(int i=0; (i<FILAS_IMG) && !termine_todo; i++)
		{
			// busco el primer cero
			if(!encontre_dist_cero){//si todavia no entontre distinto de cero me fijo que hago.
				if(m[i][j]!= 0){  // si es distinto de cero empiezo a obtener los puntos que me interesan
						encontre_dist_cero =true;
						cantDistintos++;
						res=i;
				}											
			}else{ // si ya encontre alguno distinto de cero me fijo si sigo leyendo distinto de ceros.
				if( (m[i][j]) !=0 ){ // si es distinto incrementt
					cantDistintos++;
				}else{
					termine_todo=true; // sino ya no me interesa seguir.
				}	
			}
		}				
	}				

	centro_esfera.second = res + (cantDistintos/2);
}

void obtenerCentro(matriz<uint8_t> m)
{
	obtenerCentroX(m);
	obtenerCentroY(m);
	
	#ifdef DEBUF	
	cout << "Centro esfera encontrado en X: " << centro_esfera.first << " Y: " << centro_esfera.second << endl;	
	#endif
}

void obtenerRadio(matriz<uint8_t> m)
{	
	/* Nos paramos en la columna donde esta el centro y 
	 * nos movemos hacia abajo hasta encontrar algo distinto de 0 */
	
	int columna = centro_esfera.first;
	int fila = 0;
	
	while(true)
	{
		if(m[fila][columna] == 0) fila ++;
		else break;
	}
	
	/* Calculamos el radio */
	
	radio_esfera = sqrt((columna - centro_esfera.first)*(columna - centro_esfera.first) + 
					(fila - centro_esfera.second)*(fila - centro_esfera.second));
}

bool parsearMascaraEsfera()
{
	/* Matriz donde guardaremos el archivo parseado */
	
	matriz<uint8_t> matriz_esfera(FILAS_IMG, COLUMNAS_IMG, 0);
	 	
	ifstream archivoin; 	
	archivoin.open(path_esfera_mask());
	
	if(not(archivoin.is_open()))
	{
		cout << "ERROR: No se encuentra el archivo " << path_esfera_mask() << "!" << endl;
		return false;
	}
	
	// Leemos el archivo de entrada
	
	stringstream ss_in;
  	string line = "";
	
	getline(archivoin, line);		// Leo la version	
	getline(archivoin, line);		// Leo el comentario

	ss_in << archivoin.rdbuf();
	
	int columnas, filas;
	ss_in >> columnas >> filas;		// Leo las columnas y las filas
	
	int valor;
	ss_in >> valor;					// Leo el maximo valor de la imagen	
			
	for(int fila = 0; fila < FILAS_IMG; fila++)
		for(int columna = 0; columna < COLUMNAS_IMG; columna++)
		{
			if(ss_in.eof()) 	// Si el archivo se termina antes de completar la matriz, algo anda mal...
			{
				cout << "ERROR: El archivo " << path_esfera_mask() << " contiene menos filas o columnas que las necesarias" << endl;
				archivoin.close();
				return false;
			}

			int valor;
			ss_in >> valor;			
			matriz_esfera[fila][columna] = (uint8_t) valor;
		}
		
	archivoin.close();
	
	 /* Seteamos el centro y el radio de la esfera */
 	
 	obtenerCentro(matriz_esfera);
 	obtenerRadio(matriz_esfera);
 	
 	return true;	
}

bool obtenerDireccionEsfera(int num)
{
	/* Matriz donde guardaremos el archivo parseado */
	
	matriz<uint8_t> matriz_esfera(FILAS_IMG, COLUMNAS_IMG, 0);
		 	
	ifstream archivoin; 	
	archivoin.open(path_esfera(num_imagenes[num]));
	
	if(not(archivoin.is_open()))
	{
		cout << "ERROR: No se encuentra el archivo " << path_esfera(num_imagenes[num]) << "!" << endl;
		return false;
	}
	
	// Leemos el archivo de entrada
	
	stringstream ss_in;
  	string line = "";
	
	getline(archivoin, line);		// Leo la version	
	getline(archivoin, line);		// Leo el comentario

	ss_in << archivoin.rdbuf();
	
	int columnas, filas;
	ss_in >> columnas >> filas;		// Leo las columnas y las filas
	
	int valor;
	ss_in >> valor;					// Leo el maximo valor de la imagen	
 	
	/* En esta variable guardamos las posiciones ij dentro 
	 * de la imagen donde esta el valor de mayor intesidad */
	
	int valor_pixel_max = 0;
		
	for(int fila = 0; fila < FILAS_IMG; fila++)
		for(int columna = 0; columna < COLUMNAS_IMG; columna++)
		{
			if(ss_in.eof()) 	// Si el archivo se termina antes de completar la matriz, algo anda mal...
			{
				cout << "ERROR: El archivo " << path_esfera(num_imagenes[num]) << " contiene menos filas o columnas que las necesarias" << endl;
				archivoin.close();
				return false;
			}

			ss_in >> valor;
			
			matriz_esfera[fila][columna] = (uint8_t) valor;			
			if(valor > valor_pixel_max) valor_pixel_max = valor;
		}
		
	archivoin.close();
	
	// Ya tenemos el archivo cargado en la matriz, ahora encontramos el pixel de mayor intensidad
	
	vector<pair<int, int> > vector_de_maximos;		// El par lo guardamos como <columna, fila>
	
	for(int fila = 0; fila < FILAS_IMG; fila++)
		for(int columna = 0; columna < COLUMNAS_IMG; columna++)
		{
			if((int)matriz_esfera[fila][columna] == valor_pixel_max)
				vector_de_maximos.push_back(pair<int,int>(columna, fila));
		}
		
	vector<pair<int, int> > maximos_procesados;
	
	for(unsigned int i = 0; i < vector_de_maximos.size(); i++)
	{		
		// Busco hacia arriba
		
		int fila = vector_de_maximos[i].second;
		int columna = vector_de_maximos[i].first;
		while(esMaximo((int) matriz_esfera[fila][columna], valor_pixel_max)) fila--;
		
		pair<int, int> coord_pixel_sup(columna, fila + 1);
		
		// Busco hacia abajo
		
		fila = vector_de_maximos[i].second;
		columna = vector_de_maximos[i].first;
		while(esMaximo((int) matriz_esfera[fila][columna], valor_pixel_max)) fila++;
		
		pair<int, int> coord_pixel_inf(columna, fila - 1);	
		
		// Busco hacia la izquierda
		
		fila = vector_de_maximos[i].second;
		columna = vector_de_maximos[i].first;
		while(esMaximo((int) matriz_esfera[fila][columna], valor_pixel_max)) columna--;
		
		pair<int, int> coord_pixel_izq(columna + 1, fila);	
		
		// Busco hacia la derecha
		
		fila = vector_de_maximos[i].second;
		columna = vector_de_maximos[i].first;
		while(esMaximo((int) matriz_esfera[fila][columna], valor_pixel_max)) columna++;
		
		pair<int, int> coord_pixel_der(columna - 1, fila);
		
		// Ahora busco el "centro" donde deberia estar el pixel de mayor intensidad en realidad
		
		int centro_columnas = coord_pixel_izq.first + (coord_pixel_der.first - coord_pixel_izq.first)/2;
		int centro_filas = coord_pixel_sup.second + (coord_pixel_inf.second - coord_pixel_sup.second)/2;
	
		maximos_procesados.push_back(pair<int, int>(centro_columnas, centro_filas));
	}
	
	// En esta altura tengo el "maximo" calculado obtenido del centro del rectangulo formado por blabla...
	
	int maximo_procesado = 0;
	pair<int, int> res;
	
	for(unsigned int i = 0; i < maximos_procesados.size(); i++)
	{
		pair<int,int> actual = maximos_procesados[i];
		if(maximo_procesado < matriz_esfera[actual.second][actual.first])	// Me quedo con el de valor mas grande
		{
			res = actual;
			maximo_procesado = matriz_esfera[actual.second][actual.first];
		}
	}
	
	// En 'res' tenemos las coordenadas del pixel de maxima iluminacion en formato <columna, fila>
	
	if(mostrar_debug)
	{
		cout << "Intensidad 'máxima' de la esfera " << path_esfera(num_imagenes[num]) << " encontrado en fila " ;
		cout << res.second << " columna " << res.first << endl;
	}	
 	
 	/* Calculamos ahora la direccion de la iluminacion */ 	
  	
 	if(usar_S_orig) 	// Si nos piden usar las direcciones reales, todo lo que hicimos antes no sirvio de nada
 	{
		vectores_S[num] = vectores_S_reales[atoi((num_imagenes[num]).c_str())];
	}
	else
	{
		/* Obtengo las direcciones relativas de X (columnas) e Y (filas) */
		
		double x = res.first - centro_esfera.first;
		double y = res.second - centro_esfera.second;
		double z = sqrt(radio_esfera * radio_esfera - x*x - y*y);

		tripla_dbl vec(y, x, z);
	
		double norma = norma_2(vec);
		
		vectores_S[num] = tripla_dbl(y/norma, x/norma, z/norma);
	}
 		
	if(mostrar_debug)
	{
		cout << "Vector iluminacion de la imagen " << path_imagen(tipo_imagen, num_imagenes[num]) << ":" << endl; 
		cout << "(" << vectores_S[num].fst() << ", " << vectores_S[num].snd() << ", " << vectores_S[num].trd() << ")" << endl;
	}
	 	
	return true;
}

bool obtenerDirecciones()
{
	if(!parsearMascaraEsfera())
		return false;
		
	for(int i = 0; i < CANT_IMGS; i++)
		if(!obtenerDireccionEsfera(i))
			return false;
			
	return true;
}
